package fct.mpc.utils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Random;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.android.maps.GeoPoint;

public class HuntingGrounds
{
	protected static final int margin = 500; // 0.0005 * 1E6
	
	protected static ArrayList<HuntingGround> huntingGrounds = new ArrayList<HuntingGround>();
	protected static Random randomizer = new Random();
	
	public static boolean hasHuntingGrounds()
	{
		return !huntingGrounds.isEmpty();
	}
	
	public static ArrayList<GeoPoint> getSnipeLocations()
	{
		ArrayList<GeoPoint> snipeLocations = new ArrayList<GeoPoint>();

		for(int i=0; i<huntingGrounds.size(); i++)
		{
			HuntingGround hg = huntingGrounds.get(i);
			while(!hg.hasSnipe())
			{
				int lat = (int) (hg.getMinX() + (randomizer.nextFloat() * hg.getXRange()));
				int lon = (int) (hg.getMinY() + (randomizer.nextFloat() * hg.getYRange()));
				
				if(hg.contains(lat, lon))
				{
					hg.setSnipeLocation(new GeoPoint(lat, lon));
					break;
				}
			}
			
			snipeLocations.add(hg.getSnipeLocation());
		}
		
		return snipeLocations;
	}
	
	public static boolean nearSnipe(int myLat, int myLon)
	{
		for(int i=0; i<huntingGrounds.size(); i++)
		{
			HuntingGround hg = huntingGrounds.get(i);
			if(hg.hasSnipe())
			{
				int snpLat = hg.getSnipeLocation().getLatitudeE6();
				int snpLon = hg.getSnipeLocation().getLongitudeE6();
				
				if(myLat >= snpLat - margin && myLat <= snpLat + margin && myLon >= snpLon - margin && myLon <= snpLon + margin)
					return true;
			}
		}
		
		return false;
	}
	
	public static void snipeCaught(int myLat, int myLon)
	{
		for(int i=0; i<huntingGrounds.size(); i++)
		{
			HuntingGround hg = huntingGrounds.get(i);
			if(hg.contains(myLat, myLon))
			{
				hg.removeSnipeLocation();
				break;
			}
		}
	}
	
	public static void populateHuntingGrounds(InputStream inputStream)
	{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	
		try
		{
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document dom = builder.parse(inputStream);

			Element root = dom.getDocumentElement();
			NodeList hunting_grounds = root.getElementsByTagName("hunting_ground");

			for (int i=0; i<hunting_grounds.getLength(); i++)
			{
				Node hunting_ground = hunting_grounds.item(i);
				NodeList points = hunting_ground.getChildNodes();
				
				int sides = points.getLength();
				int[] x = new int[sides];
				int[] y = new int[sides];
				
				for (int j=0; j<sides; j++)
				{
					Node point = points.item(j);
					NodeList coordinates = point.getChildNodes();
					
					for(int w=0; w<coordinates.getLength(); w++)
					{
						String name = coordinates.item(w).getNodeName();

						if (name.equalsIgnoreCase("x"))
							x[j] = (int)(Double.parseDouble(coordinates.item(w).getNodeValue()) * 1E6);
						else if(name.equalsIgnoreCase("y"))
							y[j] = (int)(Double.parseDouble(coordinates.item(w).getNodeValue()) * 1E6);
					}
				}

				huntingGrounds.add(new HuntingGround(x, y, sides));
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}
	
	private static class HuntingGround
	{
		protected boolean hasSnipe;
		
		protected Polygon area;
		protected GeoPoint snipeLocation;
		
		public HuntingGround(int[] x, int[] y, int sides)
		{
			this.area = new Polygon(x, y, sides);
			
			this.snipeLocation = null;
			this.hasSnipe = false;
		}

	    public int getMinX()
	    {
	    	return area.minX();
	    }
	    
	    public int getMinY()
	    {
	    	return area.minY();
	    }
	    
	    public int getXRange()
	    { 
	        return Math.abs(area.maxX() - area.minX()); 
	    }
	    
	    public int getYRange()
	    { 
	        return Math.abs(area.maxY() - area.minY()); 
	    }
	    
	    public GeoPoint getSnipeLocation()
	    {
	    	return this.snipeLocation;
	    }
	    
	    public void setSnipeLocation(GeoPoint point)
	    {
	    	this.snipeLocation = point;
	    	this.hasSnipe = true;
	    }
	    
	    public void removeSnipeLocation()
	    {
	    	this.snipeLocation = null;
	    	this.hasSnipe = false;
	    }
	    
	    public boolean hasSnipe()
	    {
	    	return this.hasSnipe;
	    }

	    public boolean contains(int latitude, int longitude)
	    { 
	    	return area.contains(latitude, longitude); 
	    }
	}
}